package com.luotao.test2;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

/**
 * User: luotao
 * Date: 12-6-21
 * Time: 15:16
 */
public class NIOServer {
private ByteBuffer m_buffer = ByteBuffer.allocateDirect(1024);
    private static HashMap<Integer, ChannelState> idToChannelState = new HashMap<Integer, ChannelState>();

    public void connect(int port) throws Exception {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        StringBuffer sBuffer = null;
        while (true) {
            System.out.println("loop");
            int n = selector.select();
            System.out.println("loop end");
            if (n <= 0){
                continue;
            }
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                if (idToChannelState.get(key.channel().hashCode()) != null) {

                    ChannelState state = idToChannelState.get(key.channel().hashCode());
                    if (state != null) {
                        if (!state.isStart()) {
                            // haven't start yet, change it and save the start
                            // time
                            state.setStart(true);
                            state.setStartTime(System.currentTimeMillis());
                        }
                        sBuffer = state.getBuffer();
                    }

                } else {
                    // no state for this channel now, create a state
                    // and subscribe it
                    ChannelState state = new ChannelState();
                    sBuffer = state.getBuffer();
                    idToChannelState.put(key.channel().hashCode(), state);
                }

                if (key.isAcceptable()) {
                    SocketChannel socketChannel = ((ServerSocketChannel) key
                            .channel()).accept();
                    if (socketChannel != null) {
                        // new connection from client, register it
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        sendMessageToClient(socketChannel,"Hello, welcome to NIO server, please enter your message and end it with a CRLF");
                    }
                }

                if (key.isReadable()) {
                    int count;
                    SocketChannel channel = (SocketChannel) key.channel();
                    m_buffer.clear(); // empty the buffer
                    while ((count = channel.read(m_buffer)) > 0) {

                        m_buffer.flip(); // flip the buffer to make it readable
                        while (m_buffer.hasRemaining()) {
                            int size = m_buffer.remaining();
                            byte[] privateBuffer = new byte[size];
                            m_buffer.get(privateBuffer);
                            String tempStr = new String(privateBuffer);
                            System.out.print(tempStr);
                            if (tempStr.endsWith("\n")){ // tested for end, just
                            // for windows
                            sBuffer.append(tempStr);
                                // one line finished
                                String finalMessage = sBuffer.toString();
                                sBuffer.delete(0, sBuffer.capacity()); // clean
                                                                        // the
                                                                        // string
                                                                        // buffer
                                // calculate the processing time
                                ChannelState state = idToChannelState
                                        .get(channel.hashCode());
                                long operatedTime = 0;
                                if (state != null) {
                                    // calculate the processing time
                                    operatedTime = System.currentTimeMillis()
                                            - state.getStartTime();
                                    // reset the start flag
                                    state.setStart(false);
                                }
                                sendMessageToClient(channel,"Client "+ channel.hashCode() + "["+new Date()+"] said: "+ finalMessage);
                            } else {
                                sBuffer.append(tempStr);
                            }
                        }

                        m_buffer.clear(); // Empty buffer
                    }

                    if (count < 0) {
                        // Close channel on EOF.
                        channel.close();
                        idToChannelState.remove(channel.hashCode());
                    }
                }
                it.remove();
            }
        }
    }

    /**
     * The method to send some messages to the client via channel.
     *
     * @param channel
     * @param message
     * @throws IOException
     */
    private void sendMessageToClient(SocketChannel channel, String message)
            throws IOException {
        ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024);
        writeBuffer.clear();
      // System.out.println("@@"+channel.hashCode()+"#"+message+"@@");
        writeBuffer.put((message + "\r\n").getBytes());
        writeBuffer.flip();

        channel.write(writeBuffer);
    }

    /**
     * @param args
     * @throws Exception
     */
    public static int port=9999;
    public static void main(String[] args) throws Exception {
        NIOServer nio = new NIOServer();
        nio.connect(port);
    }

}



